﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Storage;
using Storage.EntityModels;
using System.Data.Linq;
using System.Transactions;
using Stats.Objects;

namespace Stats
{
    /// <summary>
    /// This class contains the method called when a feedback is released.
    /// </summary>
    static class UpdateFeedbackModule
    {
        /// <summary>
        /// This method manages the released feedback..updates the user averages entry in UserFeedbacks table, removes the related pending feedback,
        /// updates the daily stats table if a missed appointment is detected and inserts the released feedback in the database. If an exception is raised before the end of the method,
        /// no changes are saved in the database and the feedback is still to be left.
        /// </summary>
        /// <param name="releasedFeedback">A ReleasedFeedback object</param>
        /// <returns>bool true=UPDATEOK  false=UPDATE FAILED</returns>
        public static bool manageReleasedFeedback(ReleasedFeedback releasedFeedback)
        {
            bool updateOK = true;

            switch (releasedFeedback.ReleasedFeedbackType)
            {
                
                #region REGION CASE TYPE = 0.. RELEASED FEEDBACK FROM DRV TO PSN

                case 0:     // FEEDBACK RELEASED FROM DRV TO PSN...TYPE = 0
                    {
                        try
                        {
                            using (TransactionScope t1 = new TransactionScope())
                            {
                                TripLoaDataContext db = Storage.StorageManager.Instance.Entities;

                                var userFeedbackEntry =
                                     (from userEntry in db.UserFeedbacks
                                      where userEntry.UserId == releasedFeedback.ReceiverID
                                      select userEntry).First();

                                #region update AVGs in UserFeedbacks table

                                float avgVote = userFeedbackEntry.AsPassengerAVGVote;
                                float avgPun = userFeedbackEntry.AsPassengerAVGPunctuality;
                                float avgRel = userFeedbackEntry.AsPassengerAVGReliability;
                                int numFeedLeft = userFeedbackEntry.NumberOfJourneysWithFeedbackAsPassenger;

                                userFeedbackEntry.AsPassengerAVGVote = (avgVote * numFeedLeft + releasedFeedback.GeneralVote) / (numFeedLeft + 1);
                                userFeedbackEntry.AsPassengerAVGPunctuality = (avgPun * numFeedLeft + releasedFeedback.Punctuality) / (numFeedLeft + 1);
                                userFeedbackEntry.AsPassengerAVGReliability = (avgRel * numFeedLeft + releasedFeedback.Reliability) / (numFeedLeft + 1);
                                userFeedbackEntry.NumberOfJourneysWithFeedbackAsPassenger = numFeedLeft + 1;

                                #endregion

                                #region insert released feedback in PassengerFeedbacks table

                                /* Inserts the released feedback in the PassengerFeedbacks table */

                                PassengerFeedback newPsnFeedbackToInsert = new PassengerFeedback();

                                newPsnFeedbackToInsert.GenericVote = (byte)releasedFeedback.GeneralVote;
                                newPsnFeedbackToInsert.PunctualityVote = (byte?)releasedFeedback.Punctuality;
                                newPsnFeedbackToInsert.ReliabilityVote = (byte?)releasedFeedback.Reliability;
                                newPsnFeedbackToInsert.Comment = releasedFeedback.Comment;
                                newPsnFeedbackToInsert.SenderUserId = releasedFeedback.SenderID;
                                newPsnFeedbackToInsert.ReceiverUserId = releasedFeedback.ReceiverID;
                                newPsnFeedbackToInsert.WasPresent = releasedFeedback.WasPresent;
                                newPsnFeedbackToInsert.TripId = releasedFeedback.TripID;

                                db.PassengerFeedbacks.InsertOnSubmit(newPsnFeedbackToInsert);

                                #endregion

                                #region remove released feedback from pending feedbacks table

                                /* Looks for the released feedback in the PendingFeedbacks table..if exists is removed 
                                 * else throws an InvalidOperation exception */

                                var pendingFeedbackReleased =
                                    (from pendingFeedbackEntry in db.PendingFeedbacks
                                     where pendingFeedbackEntry.TripId == releasedFeedback.TripID &&
                                             pendingFeedbackEntry.SenderUserId == releasedFeedback.SenderID &&
                                             pendingFeedbackEntry.ReceiverUserId == releasedFeedback.ReceiverID
                                     select pendingFeedbackEntry).First();

                                #endregion

                                #region update missed appointments in daily stats

                                DateTime tripDepartureDate = (DateTime)AuxTripMethods.getTripDepartureDate(releasedFeedback.TripID);

                                var dateEntry =
                                    (from entry in db.DailyStatistics
                                     where entry.Date.Year.Equals(tripDepartureDate.Year)
                                         && entry.Date.Month.Equals(tripDepartureDate.Month)
                                         && entry.Date.Day.Equals(tripDepartureDate.Day)
                                     select entry);

                                /* If the entry for the trip departure date not exists in daily table... */

                                if (dateEntry.Count() == 0)
                                {
                                    /* Inserts the entry for trip departure date and increments the missed appointments number by passengers in it*/

                                    if (releasedFeedback.WasPresent == false)
                                    {
                                        updateOK = UpdateDailyStatsTableModule.updateDailyStatsTable(tripDepartureDate, false, true);
                                    }

                                    /* Inserts the entry for trip departure date only without incrementing the missed appointments count */

                                    else
                                    {
                                        updateOK = UpdateDailyStatsTableModule.updateDailyStatsTable(tripDepartureDate, false, false);
                                    }
                                }

                                /* The date entry already exists...*/

                                else
                                {
                                    /* is updated the "missed appointments by passengers" field only */

                                    if (releasedFeedback.WasPresent == false)
                                    {
                                        dateEntry.First().MissedAppointmentsByPassengers += 1;
                                    }
                                }

                                #endregion

                                db.PendingFeedbacks.DeleteOnSubmit(pendingFeedbackReleased);

                                db.SubmitChanges();

                                t1.Complete();
                            }
                        }
                        catch (InvalidOperationException) { updateOK = false; }
                        catch (TypeInitializationException) { updateOK = false; }
                        catch (NullReferenceException) { updateOK = false; }
                        catch (System.Data.SqlClient.SqlException) { updateOK = false; }
                        catch (TransactionAbortedException) { updateOK = false; }

                        break;
                    }

                #endregion

                #region REGION CASE TYPE = 1.. RELEASED FEEDBACK FROM PSN TO DRV
                
                case 1:  
                {
                    try
                    {
                        using (TransactionScope tr2 = new TransactionScope())
                        {

                            TripLoaDataContext db = StorageManager.Instance.Entities;

                            var userFeedbackEntry =
                                 (from userEntry in db.UserFeedbacks
                                  where userEntry.UserId == releasedFeedback.ReceiverID
                                  select userEntry).First();

                            #region update AVGs in UserFeedbacks table

                            float avgVote = userFeedbackEntry.AsDriverAVGVote;
                            float avgPun = userFeedbackEntry.AsDriverAVGPunctuality;
                            float avgRel = userFeedbackEntry.AsDriverAVGReliability;
                            float avgKin = userFeedbackEntry.AsDriverAVGKindness;
                            int numFeedLeft = userFeedbackEntry.NumberOfJourneysWithFeedbackAsDriver;

                            userFeedbackEntry.AsDriverAVGVote = (avgVote * numFeedLeft + releasedFeedback.GeneralVote) / (numFeedLeft + 1);
                            userFeedbackEntry.AsDriverAVGPunctuality = (avgPun * numFeedLeft + releasedFeedback.Punctuality) / (numFeedLeft + 1);
                            userFeedbackEntry.AsDriverAVGReliability = (avgRel * numFeedLeft + releasedFeedback.Reliability) / (numFeedLeft + 1);
                            userFeedbackEntry.AsDriverAVGKindness = (avgKin * numFeedLeft + releasedFeedback.Courtesy) / (numFeedLeft + 1);
                            userFeedbackEntry.NumberOfJourneysWithFeedbackAsDriver = numFeedLeft + 1;

                            #endregion

                            #region insert released feedback in DriverFeedbacks table

                            /* Inserts the released feedback in the DriverFeedbacks table */

                            DriverFeedback newFeedbackToInsert = new DriverFeedback();

                            newFeedbackToInsert.GenericVote = (byte)releasedFeedback.GeneralVote;
                            newFeedbackToInsert.PunctualityVote = (byte?)releasedFeedback.Punctuality;
                            newFeedbackToInsert.PermissivenessVote = (byte?)releasedFeedback.Courtesy;
                            newFeedbackToInsert.ReliabilityVote = (byte?)releasedFeedback.Reliability;
                            newFeedbackToInsert.Comment = releasedFeedback.Comment;
                            newFeedbackToInsert.SenderUserId = releasedFeedback.SenderID;
                            newFeedbackToInsert.ReceiverUserId = releasedFeedback.ReceiverID;
                            newFeedbackToInsert.WasPresent = releasedFeedback.WasPresent;
                            newFeedbackToInsert.TripId = releasedFeedback.TripID;

                            db.DriverFeedbacks.InsertOnSubmit(newFeedbackToInsert);

                            #endregion

                            #region remove released feedback from pending feedbacks table

                            /* Looks for the released feedback in the PendingFeedbacks table..if exists is removed 
                                 * else throws an InvalidOperation exception */

                            var pendingFeedbackReleased =
                                (from pendingFeedbackEntry in db.PendingFeedbacks
                                 where pendingFeedbackEntry.TripId == releasedFeedback.TripID &&
                                         pendingFeedbackEntry.SenderUserId == releasedFeedback.SenderID &&
                                         pendingFeedbackEntry.ReceiverUserId == releasedFeedback.ReceiverID
                                 select pendingFeedbackEntry).First();

                            #endregion

                            #region update missed appointments in daily stats

                            DateTime tripDepartureDate = (DateTime)AuxTripMethods.getTripDepartureDate(releasedFeedback.TripID);

                            var dateEntry =
                                (from entry in db.DailyStatistics
                                 where entry.Date.Year.Equals(tripDepartureDate.Year)
                                     && entry.Date.Month.Equals(tripDepartureDate.Month)
                                     && entry.Date.Day.Equals(tripDepartureDate.Day)
                                 select entry);

                            /* If the entry for the trip departure date not exists in daily table... */

                            if (dateEntry.Count() == 0)
                            {
                                /* Inserts the entry for trip departure date and increments the missed appointments number by drivers in it*/

                                if (releasedFeedback.WasPresent == false)
                                {
                                    updateOK = UpdateDailyStatsTableModule.updateDailyStatsTable(tripDepartureDate, true, false);
                                }

                                /* Inserts the entry for trip departure date only without incrementing the missed appointments count */

                                else
                                {
                                    updateOK = UpdateDailyStatsTableModule.updateDailyStatsTable(tripDepartureDate, false, false);
                                }
                            }

                            /* The date entry already exists...*/ 

                            else
                            {
                                /* is updated the "missed appointments by drivers" field only */

                                if (releasedFeedback.WasPresent == false)
                                {
                                    dateEntry.First().MissedAppointmentsByDrivers += 1;
                                }
                            }
                            
                            #endregion

                            db.PendingFeedbacks.DeleteOnSubmit(pendingFeedbackReleased);

                            db.SubmitChanges();

                            tr2.Complete();

                        }
                               
                    }
                    catch (InvalidOperationException) { updateOK = false; }
                    catch (TypeInitializationException) { updateOK = false; }
                    catch (NullReferenceException) { updateOK = false; }
                    catch (System.Data.SqlClient.SqlException) { updateOK = false; }

                    break;
                }

                #endregion

                #region REGION CASE DEFAULT

                default:
                {
                    updateOK = false;
                    break;
                }

                #endregion

            }

            return updateOK;
        }

        ///// <summary>
        ///// This method inserts a negative feeback for the owner automatically from the system. 
        ///// This method will be used by groupware when an owner deletes the trip few moments or few days before the trip departure date.
        ///// </summary>
        ///// <param name="ownerID">The ID of the owner who deletes the trip</param>
        ///// <param name="tripID">The ID of the deleted trip by the owner</param>
        ///// <param name="senderID">The sender ID...(System ID??)</param>
        ///// <returns>A boolean that indicates the operation status. TRUE = OK...FALSE = ERROR</returns>
        //public static bool insertNegativeFeedbackForOwnerOperation(int ownerID, int tripID, int senderID)
        //{
        //    int negativeVote = StatsModule.negativeFeedbackValue;

        //    ReleasedFeedback rf = new ReleasedFeedback(senderID, ownerID, negativeVote, negativeVote, negativeVote, negativeVote, StatsModule.negativeCommentForOwner, tripID, 1, false);
        //    bool operationOK = true;

        //    //try
        //    //{
        //        operationOK = StatsModule.insertReleasedFeedback(rf);
        //    //}
        //    //catch (InvalidOperationException) { operationOK = false; }
        //    //catch (TypeInitializationException) { operationOK = false; }
        //    //catch (NullReferenceException) { operationOK = false; }
        //    //catch (System.Data.SqlClient.SqlException) { operationOK = false; }
        //    //catch (TransactionAbortedException) { operationOK = false; }

        //    return operationOK;

        //}

        ///// <summary>
        ///// This method inserts a negative feeback for the passenger automatically from the system. 
        ///// This method will be used by groupware when a passenger deletes his reservation from the trip few moments or few days before the trip departure date.
        ///// </summary>
        ///// <param name="psnID">The ID of the passenger who cancels his reservation from the trip</param>
        ///// <param name="tripID">The ID of the trip whence the passenger cancels his reservation</param>
        ///// <param name="senderID">The sender ID...(System ID??)</param>
        ///// <returns>A boolean that indicates the operation status. TRUE = OK...FALSE = ERROR</returns>
        //public static bool insertNegativeFeedbackForPassengerOperation(int psnID, int tripID, int senderID)
        //{
        //    int negativeVote = StatsModule.negativeFeedbackValue;

        //    ReleasedFeedback rf = new ReleasedFeedback(senderID, psnID, negativeVote, negativeVote, negativeVote, negativeVote, StatsModule.negativeCommentForPassenger, tripID, 0, false);
        //    bool operationOK = true;

        //    try
        //    {
        //        operationOK = StatsModule.insertReleasedFeedback(rf);
        //    }
        //    catch (InvalidOperationException) { operationOK = false; }
        //    catch (TypeInitializationException) { operationOK = false; }
        //    catch (NullReferenceException) { operationOK = false; }
        //    catch (System.Data.SqlClient.SqlException) { operationOK = false; }
        //    catch (TransactionAbortedException) { operationOK = false; }

        //    return operationOK;

        //}
    
    
    
    }
}
